#include <LiquidCrystal.h>
#include <Ethernet.h>
#include <Server.h>
#include <Udp.h>
#include <SPI.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <SD.h>
#define BUFSIZ 128

// Setup NIC
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x30, 0x92 };
byte ip[] = { 192,168,100,90  };
char rootFileName[] = "index.htm";
Server server(80);

// SD Card
// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;
Sd2Card card;
SdVolume volume;
SdFile file;
SdFile root;

// NTP Time stuff
unsigned int localPort = 8888;  // local port to listen for UDP packets
//byte timeServer[] = {192, 43, 244, 18}; // time.nist.gov NTP server
byte timeServer[] = {192, 168, 100, 5};  // time server ip address
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 

const int buttonPin = 2;    // the pin that the pushbutton is attached to
const double demandConstant = .72 * 4; //constant from utility to multiply count
const int demandChargeTime = 15; //number of minutes utility bases demand
const int demandLimit = 100; //number to warn when demand goes higher
double currentDemand = 0; // Current kW in last XX minutes
int demandLoopCount = 0;
int countArray[demandChargeTime];

LiquidCrystal lcd(9, 3, 8, 14, 5, 6, 7); //RS,RW,E,d4,d5,d6,d7
//int backLight = 2;    // will control the backlight

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  //setup LCD
  lcd.begin(16, 2);//setup coloumn and rows
  lcd.clear();
 
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  server.begin();
  
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);

  Serial.begin(9600);
  
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  lcd.setCursor(0,0);
  lcd.clear();
  lcd.print("Initial SD");
  lcd.clear();
 
  PgmPrint("Free RAM: ");
  Serial.println(FreeRam());  

  // Initialize SD Card
  pinMode(10, OUTPUT);              
  digitalWrite(10, HIGH);              

  if (!card.init(SPI_HALF_SPEED, 4)) 
  { 
    lcd.print("SD Error - Card");
  }  
  if (!volume.init(&card)) 
  {
    lcd.print("SD Error - Vol");
  }
  
  PgmPrint("Volume is FAT");
  Serial.println(volume.fatType(),DEC);
  Serial.println();

  if (!root.openRoot(&volume)) Serial.print("openRoot failed");
  
  PgmPrintln("Files found in all dirs:");
  root.ls(LS_R);
  
  lcd.clear();
  lcd.print("SD card OK");
  delay(1000);
  
  lcd.clear();
  lcd.print("Syncing Time...");  
  
  //sync time to NTP
  getNTP();

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(month());
  lcd.print("/");
  lcd.print(day());
  lcd.print("/");
  lcd.print(year());
  lcd.setCursor(0,1);
  lcd.print(hour());
  lcd.print(":");
  lcd.print(minute());
  lcd.print(" EST");
  delay(3000);
  lcd.clear();
    
  Alarm.timerRepeat(5, Demand); //Run demand loop every 60 secs, 5 for testing
 // Alarm.timerRepeat(8, logData); //Run LogData every 5min = 300
    
}

void loop() {
   
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      lcd.setCursor(0,1);
      lcd.print("*");
    } 
    else {
      // if the current state is LOW then the button wend from on to off:
     lcd.setCursor(0,1);
     lcd.print(" ");
    }
  }
  //save the current state as the last state,for next time through the loop
  lastButtonState = buttonState;

  //need this for repeated loops to work
  Alarm.delay(1);
  
  webServer();
}

void Demand()
{
  if(demandLoopCount <= (demandChargeTime-2))
  {  
    countArray[demandLoopCount] = buttonPushCounter;
    demandLoopCount++;
  }
  else {
    for(int i=0; i <= (demandChargeTime-2); i++)
    {
      countArray[i] = countArray[i+1];
    }
    
    //demandLoopCount should be 15 here
    countArray[(demandChargeTime-1)] = buttonPushCounter;
    currentDemand = demandConstant * (countArray[(demandChargeTime-1)]-countArray[0]);

    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Demand ");
    lcd.print(currentDemand);
    lcd.print(" kW");
  }
}

void webServer()
{
  char clientline[BUFSIZ];
  char *filename;
  int image = 0;
  int index = 0;

  Client client = server.available();
  if (client) {
    boolean current_line_is_blank = true;

    index = 0;

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;

          continue;
        }

        clientline[index] = 0;
        filename = 0;

        Serial.println(clientline);

        if (strstr(clientline, "GET / ") != 0) {
          filename = rootFileName;

        } 
        if (strstr(clientline, "GET /") != 0) {
          if (!filename) filename = clientline + 5; 

          (strstr(clientline, " HTTP"))[0] = 0;

          Serial.println(filename);

          if (! file.open(&file, filename, O_READ)) {
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>Error 404</h2>");
            client.println("<s2>The file does not exist.<s2>");
            client.println("");
            break;
          }

          Serial.println("Opened!");
          //File types
          client.println("HTTP/1.1 200 OK");
          if (strstr(filename, ".htm") != 0)
            client.println("Content-Type: text/html");
          else if (strstr(filename, ".css") != 0)
            client.println("Content-Type: text/css");
          else if (strstr(filename, ".png") != 0)
            client.println("Content-Type: image/png");
          else if (strstr(filename, ".jpg") != 0)
            client.println("Content-Type: image/jpeg");
          else if (strstr(filename, ".gif") != 0)
            client.println("Content-Type: image/gif");
          else if (strstr(filename, ".3gp") != 0)
            client.println("Content-Type: video/mpeg");
          else if (strstr(filename, ".pdf") != 0)
            client.println("Content-Type: application/pdf");
          else if (strstr(filename, ".js") != 0)
            client.println("Content-Type: application/x-javascript");
          else if (strstr(filename, ".xml") != 0)
            client.println("Content-Type: application/xml");
          else
            client.println("Content-Type: text");
          client.println();

          int16_t c;
          while ((c = file.read()) >= 0) {
            //Serial.print((char)c); //Prints all HTML code to serial (For debuging)
            client.print((char)c); //Prints all HTML code for web page
          }


          // client.print("end");

          file.close();

        } 
        else {
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>Error 404</h2>");
          client.println("");
        }
        break;
      }
    }

    delay(1);

    client.stop();
  }
//  server.begin();
//  
//  // listen for incoming clients
//  Client client = server.available();
//  if (client) {
//    // an http request ends with a blank line
//    boolean currentLineIsBlank = true;
//    while (client.connected()) {
//      if (client.available()) {
//        char c = client.read();
//        // if you've gotten to the end of the line (received a newline
//        // character) and the line is blank, the http request has ended,
//        // so you can send a reply
//        if (c == '\n' && currentLineIsBlank) {
//          // send a standard http response header
//          client.println("HTTP/1.1 200 OK");
//          client.println("Content-Type: text/html");
//          client.println();
//          client.println("<center><b>Demand Meter</b></center>");
//          client.print("<br>");
//          client.print("Demand ");
//          client.print(currentDemand);
//          client.print(" kW");
//          client.print("<br><br>");
//          client.print("<a href=\"/logs.html\">View Logs</a>");          
//          client.print("<br><br>");
//          client.print(month());
//          client.print("/");
//          client.print(day());
//          client.print("/");
//          client.print(year());
//          client.print("<br>");
//          client.print(hour());
//          client.print(":");
//          client.print(minute());
//          client.print(":");
//          client.print(second());
//          client.println();
//                 
//          break;
//        }
//        if (c == '\n') {
//          // you're starting a new line
//          currentLineIsBlank = true;
//        } 
//        else if (c != '\r') {
//          // you've gotten a character on the current line
//          currentLineIsBlank = false;
//        }
//      }
//    }
//    // give the web browser time to receive the data
//    delay(1);
//    // close the connection:
//    client.stop();
//  }
}


void logData()
{
 if (demandLoopCount == 14)
 {
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    
    String fixMonth = String(month());
    String fixDay = String(day());
  
    if (month() < 10)
    {  
      fixMonth = "0" + String(month());           
    }
    if (day() < 10)
    {  
      fixDay = "0" + String(day());   
    }
       
    String filename = String(year()) + fixMonth + fixDay + ".csvv";
    
    char filename1[filename.length()];
    filename.toCharArray(filename1, filename.length());
    
    File dataFile = SD.open(filename1, FILE_WRITE);
    // if the file is available, write to it:
    if (dataFile) {
      String time =  String(hour()) + ":" + String(minute());
      String data = time + "," +  tempToAscii(currentDemand);  
      dataFile.println(data);
      dataFile.close();
      lcd.setCursor(15,1);
      lcd.print("|");
      delay(1000);
      lcd.setCursor(15,1);
      lcd.print(" ");
      Serial.println(data);
  }  
  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("Error");
  } 
 }
}

void getNTP()
{
  Udp.begin(localPort);
    
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000);  
  
  if ( Udp.available() ) {  
    Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
            
    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;  
      
    setTime(epoch);
    int dst = hour() - 5; //adjust for standard time
    setTime(dst,minute(),second(),day(),month(),year());
    
  }
}

// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}

char* tempToAscii(float temp)
// convert long to type char and store in variable array ascii
{
  char ascii[20];// needs to be this big to hold a type float

  int frac;
  int rnd;

    rnd = (unsigned int)(temp*1000)%10;
    frac=(unsigned int)(temp*100)%100;  //get three numbers to the right of the deciaml point.
    if (rnd>=5) frac=frac+1;
    itoa((int)temp,ascii,10);           // I changed it to 2 decimal places
    strcat(ascii,".");

  if (frac<10)  {itoa(0,&ascii[strlen(ascii)],10); }   // if fract < 10 should print .0 fract ie if fract=6 print .06

    itoa(frac,&ascii[strlen(ascii)],10); //put the frac after the deciaml
    
  return ascii;
} 



